package parser

import (
	"errors"
	"go/ast"
	"go/parser"
	"go/token"
	"io/fs"
	"os"
	"path"
	"path/filepath"
	"strings"

	"gitee.com/lipore/plume/db_gorm/dal/models"
)

func ParsePackage(dir string) ([]*models.Package, error) {
	dir, err := filepath.Abs(dir)
	if err != nil {
		return nil, err
	}
	project, err := ParseProject(dir)
	if err != nil {
		return nil, err
	}
	fileset := token.NewFileSet()
	f, err := parser.ParseDir(fileset, dir, func(fileInfo fs.FileInfo) bool {
		if strings.HasSuffix(fileInfo.Name(), ".dal.go") {
			return false
		}
		return true
	}, parser.ParseComments)
	if err != nil {
		return nil, err
	}
	packages := make([]*models.Package, 0)
	for _, pkg := range f {
		if strings.HasSuffix(pkg.Name, "_test") || pkg.Name == "main" {
			continue
		}
		p := models.NewPackage(pkg.Name, project.PackagePath(pkg))
		ast.Walk(p, pkg)
		packages = append(packages, p)
	}
	return packages, nil
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

type ProjectInfo struct {
	Path  string
	Model string
}

func (p *ProjectInfo) PackagePath(pkg *ast.Package) string {
	for name, _ := range pkg.Files {
		return strings.Replace(path.Dir(name), p.Path, p.Model, 1)
	}
	return ""
}

func ParseProject(dir string) (*ProjectInfo, error) {
	projectPath := dir
	basePackageName := ""
	for {
		if fileExists(filepath.Join(projectPath, "go.mod")) {
			gomod, err := os.ReadFile(filepath.Join(projectPath, "go.mod"))
			if err != nil {
				return nil, err
			}
			modlines := strings.Split(string(gomod), "\n")
			for _, line := range modlines {
				if strings.HasPrefix(line, "module ") {
					basePackageName = strings.TrimSpace(strings.TrimPrefix(line, "module "))
					break
				}
			}
			break
		}
		projectPath = filepath.Dir(projectPath)
		if projectPath == "/" {
			return nil, errors.New("not a go project")
		}
	}
	return &ProjectInfo{
		Path:  projectPath,
		Model: basePackageName,
	}, nil
}
